package com.tdk.mybatisplus.demo.common.util;


import static com.tdk.mybatisplus.demo.common.entity.Constants.IMPORT_MAX_ROW_SIZE;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.tdk.mybatisplus.demo.common.exception.GlobalException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.web.multipart.MultipartFile;

/**
 * @Description excel工具类
 * @Date 2022/5/17 00:39
 * @Author dengningcheng
 **/
@Slf4j
public final class ExcelUtil {

  private ExcelUtil() {
  }

  /**
   * <p>
   * 写excel
   * </p>
   *
   * @param response
   * @param data
   * @param fileName
   * @param sheetName
   * @param head
   * @author: taodingkai
   * @since: 2022/5/18 10:57
   */
  public static void writeExcel(
      @NotNull HttpServletResponse response, @NotNull List<List<String>> head, @NotNull List<List<Object>> data,
      @NotNull String fileName, @NotNull String sheetName) throws Exception {
    //输出流
    OutputStream outputStream = getOutputStream(fileName, response);
    //写excel
    writeExcel(outputStream, head, data, sheetName);
  }

  /**
   * <p>
   * 读取excel
   * </p>
   *
   * @param file
   * @param dataMapList
   * @param headMapList
   * @author: taodingkai
   * @since: 2022/5/18 11:10
   */
  public static void readExcelFile(
      @NotNull MultipartFile file, @NotNull List<Map<Integer, String>> dataMapList,
      @NotNull List<Map<Integer, String>> headMapList) throws Exception {
    //默认header是第一行,默认需要数据
    readExcelFileWithHeaderRowNumbers(file, dataMapList, headMapList, 1,
        Boolean.TRUE);
  }

  /**
   * <p>
   * 读取excel
   * </p>
   *
   * @param file
   * @param dataMapList
   * @param headMapList
   * @author: taodingkai
   * @since: 2022/5/18 11:10
   */
  public static void readExcelFileWithHeaderRowNumbers(
      @NotNull MultipartFile file, @NotNull List<Map<Integer, String>> dataMapList,
      @NotNull List<Map<Integer, String>> headMapList, @NotNull int headRowNumber, @NotNull Boolean isNeedData)
      throws Exception {
    readExcelFileWithHeaderRowNumbers(file.getInputStream(), dataMapList, headMapList, headRowNumber, isNeedData);
  }

  /**
   * <p>
   * 读取excel
   * </p>
   *
   * @param inputStream
   * @param dataMapList
   * @param headMapList
   * @author: taodingkai
   * @since: 2022/5/18 11:10
   */
  public static void readExcelFileWithHeaderRowNumbers(
      @NotNull InputStream inputStream, @NotNull List<Map<Integer, String>> dataMapList,
      @NotNull List<Map<Integer, String>> headMapList, @NotNull int headRowNumber, @NotNull Boolean isNeedData) {
    try {
      EasyExcel.read(inputStream,
          new AnalysisEventListener<Map<Integer, String>>() {
            @Override
            public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
              //把heade存起来
              headMapList.add(headMap);
            }

            @Override
            public void invoke(Map<Integer, String> dataMap, AnalysisContext analysisContext) {
              if (isNeedData) {
                //把data存起来
                dataMapList.add(dataMap);

              } else {
                //上层接口onException的默认行为就是抛出异常,所以这里直接抛出异常，就停止执行了
                throw new ExcelAnalysisStopException();
              }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
              if (dataMapList.size() > IMPORT_MAX_ROW_SIZE) {
                throw new GlobalException("数据导入失败，文件导入每次最多导入" + IMPORT_MAX_ROW_SIZE + "条，请重试！");
              } else {
                log.info("数据导入成功!");
              }
            }
          }
      )
          .headRowNumber(headRowNumber)
          .sheet(0)
          .registerConverter(new SqlDateStringConverter())
          .registerConverter(new TimestampStringConverter())
          .doRead();
    } catch (Exception e) {
      if (e instanceof ExcelAnalysisStopException) {
        // 数据读取完成
      } else {
        // 抛出异常
        throw e;
      }
    }
  }

  /**
   * <p>
   * 写excel
   * </p>
   *
   * @param outputStream
   * @param data
   * @param sheetName
   * @param head
   * @author: taodingkai
   * @since: 2022/5/18 10:57
   */
  public static void writeExcel(
      @NotNull OutputStream outputStream, @NotNull List<List<String>> head, @NotNull List<List<Object>> data,
      @NotNull String sheetName) {
    //输出流
    //写excel
    EasyExcel.write(outputStream)
        .head(head)
        .excelType(ExcelTypeEnum.XLSX)
        .sheet(sheetName)
        //默认样式
        .registerWriteHandler(getDefaultStyle())
        //自动列宽
        .registerWriteHandler(new CustomColumnWidthStyleStrategy())
        .registerConverter(new SqlDateStringConverter())
        .registerConverter(new TimestampStringConverter())
        .doWrite(data);
  }


  /**
   * <p>
   * 默认样式
   * </p>
   *
   * @return com.alibaba.excel.write.style.HorizontalCellStyleStrategy
   * @author: taodingkai
   * @since: 2022/5/18 10:57
   */
  private static HorizontalCellStyleStrategy getDefaultStyle() {
    WriteCellStyle headWriteCellStyle = new WriteCellStyle();
    headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
    WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
    contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
    return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
  }

  /**
   * <p>
   * 获取输出流
   * </p>
   *
   * @param file2Name
   * @param response
   * @return java.io.OutputStream
   * @author: taodingkai
   * @since: 2022/5/18 10:55
   */
  private static OutputStream getOutputStream(@NotNull String file2Name, @NotNull HttpServletResponse response)
      throws Exception {
    String fileName = URLEncoder.encode(file2Name, StandardCharsets.UTF_8.name());
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding("utf8");
    response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
    return response.getOutputStream();
  }

  /**
   * <p>
   * Map<Integer, String>转List<String>
   * </p>
   *
   * @param map
   * @return java.util.List<java.lang.String>
   * @author: taodingkai
   * @since: 2022/5/20 13:06
   */
  public static List<String> indexValueMap2List(Map<Integer, String> map) {
    List<String> orderValueMapping = initIndexValueMappingList(map.size());
    map.keySet().stream().forEach(key -> orderValueMapping.set(key, map.get(key)));
    return orderValueMapping;
  }

  /**
   * <p>
   * Map<Integer, E>转List<Object>
   * </p>
   *
   * @param map
   * @return java.util.List<java.lang.Object>
   * @author: taodingkai
   * @since: 2022/5/20 13:06
   */
  public static List<Object> indexValueMap2ListObject(Map<Integer, String> map) {
    List<Object> orderValueMapping = initIndexValueMappingList(map.size());
    map.keySet().stream().forEach(key -> orderValueMapping.set(key, map.get(key)));
    return orderValueMapping;
  }

  /**
   * <p>
   * 初始化 initIndexValueMappingList
   * </p>
   *
   * @param size
   * @return java.util.List<R>
   * @author: taodingkai
   * @since: 2022/5/20 13:29
   */
  public static <R> List<R> initIndexValueMappingList(int size) {
    List<R> indexValueMappingList = new ArrayList<R>(size);
    //初始化list
    for (int i = 0; ; i++) {
      indexValueMappingList.add(i, null);
      if (Objects.equals(indexValueMappingList.size(), size)) {
        break;
      }
    }
    return indexValueMappingList;
  }

  /**
   * <p>
   * List<Map<Integer, String>>转List<List<String>>
   * </p>
   *
   * @param dataMapList
   * @return java.util.List<java.util.List < java.lang.String>>
   * @author: taodingkai
   * @since: 2022/5/20 13:04
   */
  public static List<List<String>> indexValueMapList2TwoDList(List<Map<Integer, String>> dataMapList) {
    return dataMapList.stream().map(ExcelUtil::indexValueMap2List).collect(Collectors.toList());
  }

  /**
   * <p>
   * List<Map<Integer, String>>转List<List<Object>>
   * </p>
   *
   * @param dataMapList
   * @return java.util.List<java.util.List < java.lang.Object>>
   * @author: taodingkai
   * @since: 2022/5/20 13:04
   */
  public static List<List<Object>> indexValueMapList2TwoDListObject(List<Map<Integer, String>> dataMapList) {
    return dataMapList.stream().map(ExcelUtil::indexValueMap2ListObject).collect(Collectors.toList());
  }
}
